package cn.t.facade.industry;


import cn.t.constants.CommonConstants;
import cn.t.converter.industry.IndustryDeviceConverter;
import cn.t.dto.api.ProductDTO;
import cn.t.dto.order.OrderQueryRequest;
import cn.t.dto.order.QuotationQueryRequest;
import cn.t.enums.order.OrderTypeEnum;
import cn.t.enums.order.ReplenishmentStatusEnum;
import cn.t.model.device.CargowayDO;
import cn.t.model.device.CargowaySkuDO;
import cn.t.model.device.DeviceDO;
import cn.t.model.order.OrderDO;
import cn.t.model.order.OrderDetailDO;
import cn.t.model.order.ReplenishmentDO;
import cn.t.model.order.WorkOrderDO;
import cn.t.model.order.WorkOrderDetailDO;
import cn.t.service.device.CargowayService;
import cn.t.service.device.CargowaySkuService;
import cn.t.service.device.DeviceService;
import cn.t.service.order.OrderDetailService;
import cn.t.service.order.OrderService;
import cn.t.service.order.ReplenishmentService;
import cn.t.service.order.WorkOrderDetailService;
import cn.t.service.order.WorkOrderService;
import cn.t.service.sku.SkuService;
import cn.t.utils.BusinessUtil;
import cn.t.utils.DateUtil;
import cn.t.utils.JsonUtil;
import cn.t.vo.industry.IndustryCostCenterAmountVO;
import cn.t.vo.industry.IndustryDeviceSummaryVO;
import cn.t.vo.industry.IndustryDeviceVO;
import cn.t.vo.industry.IndustryOverviewVO;
import cn.t.vo.industry.IndustrySkuVO;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 领料单服务编排
 */
@Slf4j
@Service
@AllArgsConstructor
public class IndustryFacade {

    private final DeviceService deviceService;
    private final CargowayService cargowayService;
    private final CargowaySkuService cargowaySkuService;
    private final IndustryDeviceConverter industryDeviceConverter;
    private final OrderService orderService;
    private final OrderDetailService orderDetailService;
    private final WorkOrderService workOrderService;
    private final WorkOrderDetailService workOrderDetailService;
    private final SkuService skuService;
    private final ReplenishmentService replenishmentService;
    private final RedissonClient redissonClient;

    public List<IndustryDeviceVO> list(Long firmId) {

        // 查询设备
        List<DeviceDO> devices = this.deviceService.getDevices(null, firmId, null, CommonConstants.ONE);
        List<Long> deviceIds = devices.stream().map(DeviceDO::getId).collect(Collectors.toList());
        // 根据imei分组
        Map<String, List<DeviceDO>> deviceMap = devices.stream().collect(Collectors.groupingBy(DeviceDO::getImei));
        // 主设备
        List<DeviceDO> mainDevices = devices.stream().filter(f -> CommonConstants.ONE.equals(f.getIsMaster())).collect(Collectors.toList());
        // 设备货道
        List<CargowayDO> cargowayDOS = this.cargowayService.getCargowayList(deviceIds);
        // 设备商品
        List<CargowaySkuDO> cargowaySkuDOS = this.cargowaySkuService.getDeviceSkuList(deviceIds);
        Map<Long, CargowaySkuDO> cargowaySkuMap = cargowaySkuDOS.stream().collect(Collectors.toMap(CargowaySkuDO::getCargowayId, Function.identity(), (a,b) -> a));

        List<IndustryDeviceVO> industryDevices = new ArrayList<>();
        mainDevices.forEach(f -> {
            List<DeviceDO> curDevices = deviceMap.get(f.getImei());
            List<Long> curDeviceIds = curDevices.stream().map(DeviceDO::getId).collect(Collectors.toList());
            List<CargowayDO> curCargowayList = cargowayDOS.stream().filter(c -> curDeviceIds.contains(c.getDeviceId())).collect(Collectors.toList());
            List<CargowaySkuDO> curCargowaySkuList = cargowaySkuDOS.stream().filter(c -> curDeviceIds.contains(c.getDeviceId())).collect(Collectors.toList());

            List<IndustryDeviceVO.Cargoway> cargowayList = new ArrayList<>();
            for (CargowayDO cargoway:curCargowayList) {
                CargowaySkuDO cargowaySku= cargowaySkuMap.get(cargoway.getId());
                if (cargowaySku == null) {
                    continue;
                }

                int actQty = BusinessUtil.calcQty(cargowaySku);

                cargowayList.add(this.industryDeviceConverter.data2IndustryCargoway(cargowaySku, actQty, cargoway.getCargowayVol()));
            }

            // 货道容量
            int totalCurCargowayVol = curCargowayList.stream().mapToInt(CargowayDO::getCargowayVol).sum();
            // 库存总量
            int totalCurSkuInventory = CollectionUtils.isEmpty(curCargowaySkuList) ? 0 : curCargowaySkuList.stream().mapToInt(CargowaySkuDO::getInventory).sum();
            String stockRate = BusinessUtil.getStockRate(totalCurSkuInventory, totalCurCargowayVol) + "%";
            industryDevices.add(this.industryDeviceConverter.data2IndustryDevice(f, stockRate, curCargowayList.size(), cargowayList));
        });

        return industryDevices;
    }

    public IndustryOverviewVO overview(Long firmId) {
        // 查询设备
        List<DeviceDO> devices = this.deviceService.getDevices(null, firmId, null, CommonConstants.ONE);

        IndustryOverviewVO industryOverview = new IndustryOverviewVO();
        // 设备相关统计
        this.wrapDeviceInfo(devices, industryOverview);
        // 订单相关统计
        this.wrapOrderInfo(firmId, DateUtil.currentDaily(), industryOverview);
        // 补货单相关统计
        this.wrapReplenishmentInfo(firmId, industryOverview);

        return industryOverview;
    }

    public IndustryOverviewVO monthOverview(Long firmId) {
        // 查询设备
        List<DeviceDO> devices = this.deviceService.getDevices(null, firmId, null, CommonConstants.ONE);

        IndustryOverviewVO industryOverview = new IndustryOverviewVO();
        // 设备相关统计
        this.wrapDeviceInfo(devices, industryOverview);
        // 订单相关统计
        this.wrapOrderInfo(firmId, DateUtil.currentMonthly(), industryOverview);
        return industryOverview;
    }

    public IndustryCostCenterAmountVO costCenterAmount(Long firmId, Integer count) {
        // 查询上月至今工单
        LocalDateTime firstDayOfMonth = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
        List<WorkOrderDO> workOrders = workOrderService.firstPage(firmId, DateUtil.afterMonth(firstDayOfMonth, -1), Integer.MAX_VALUE);

        if (CollectionUtils.isEmpty(workOrders)) {
            return new IndustryCostCenterAmountVO(new BigDecimal("0.00"), new BigDecimal("0.00"), Collections.emptyList());
        }
        List<String> workOrderSns = workOrders.stream().map(WorkOrderDO::getWorkOrderSn).collect(Collectors.toList());
        // 查询对应的领料单
        QuotationQueryRequest orderQueryRequest = new QuotationQueryRequest();
        orderQueryRequest.setWorkOrderSns(workOrderSns);
        List<OrderDO> orders = this.orderService.orderQuoteOrders(orderQueryRequest);
        orders = orders.stream().filter(f -> f.getCostCenterId() != null).collect(Collectors.toList());
        // 根据成本中心分组
        Map<Long, List<OrderDO>> orderMap = orders.stream().collect(Collectors.groupingBy(OrderDO::getCostCenterId));
        List<OrderDetailDO> orderDetails = getOrderDetails(orders);
        // 查商品中心获取面价
        List<String> skuCodes = orderDetails.stream().map(OrderDetailDO::getSkuCode).collect(Collectors.toList());
        Map<String, BigDecimal> productPriceMap =new HashMap<>();

        // 获取当月领用总金额
        BigDecimal curMonthTotalAmount = orderDetails.stream().filter(f -> f.getGmtCreated().after(DateUtil.currentMonthly()))
                .map(f -> new BigDecimal(f.getQty()).multiply(productPriceMap.get(f.getSkuCode()))).reduce(BigDecimal.ZERO, BigDecimal::add);

        // 获取上月领用总金额
        BigDecimal lastMonthTotalAmount = orderDetails.stream().filter(f -> f.getGmtCreated().before(DateUtil.currentMonthly()))
                .map(f -> new BigDecimal(f.getQty()).multiply(productPriceMap.get(f.getSkuCode()))).reduce(BigDecimal.ZERO, BigDecimal::add);

        List<IndustryCostCenterAmountVO.Item> items = new ArrayList<>();
        orderMap.forEach((key, val) -> {
            List<OrderDO> curMonthOrders = val.stream().filter(f -> f.getGmtCreated().after(DateUtil.currentMonthly())).collect(Collectors.toList());
            List<OrderDO> lastMonthOrders = val.stream().filter(f -> f.getGmtCreated().before(DateUtil.currentMonthly())).collect(Collectors.toList());

            OrderDO order = val.get(0);
            BigDecimal amount = this.getAmount(curMonthOrders, productPriceMap, orderDetails);
            BigDecimal lastMonthAmount = this.getAmount(lastMonthOrders, productPriceMap, orderDetails);

            BigDecimal percentage = BigDecimal.ZERO.compareTo(curMonthTotalAmount) ==0  ? BigDecimal.ZERO : amount.divide(curMonthTotalAmount, 4);
            items.add(new IndustryCostCenterAmountVO.Item(order.getCostCenterId(), order.getCostCenterName(), amount, lastMonthAmount, percentage.multiply(new BigDecimal(100))));

        });

        // 根据金额倒排
        items.sort(Comparator.comparing(IndustryCostCenterAmountVO.Item::getAmount).reversed());
        // 取top x
        List<IndustryCostCenterAmountVO.Item> subItems = items.subList(0, Math.min(count - 1, items.size()));
        BigDecimal topxPercentage = subItems.stream().map(IndustryCostCenterAmountVO.Item::getPercentage).reduce(BigDecimal.ZERO, BigDecimal::add);
        // top x外的合并为其它
        if (items.size() > count - 1) {
            List<IndustryCostCenterAmountVO.Item> otherItems = items.subList(count - 1, items.size());

            BigDecimal otherAmount = otherItems.stream().map(IndustryCostCenterAmountVO.Item::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal otherLastMonthAmount = otherItems.stream().map(IndustryCostCenterAmountVO.Item::getLastMonthAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal otherPercentage = new BigDecimal(100).subtract(topxPercentage);
            subItems.add(new IndustryCostCenterAmountVO.Item(otherItems.get(0).getCostCenterId(), otherItems.get(0).getCostCenterName(), otherAmount, otherLastMonthAmount, otherPercentage));
        }

        return new IndustryCostCenterAmountVO(curMonthTotalAmount, lastMonthTotalAmount, subItems);
    }

    public List<IndustryDeviceSummaryVO> deviceSummary(Long firmId, Integer count) {
        List<IndustryDeviceSummaryVO> industryDeviceSummaryVOS = new ArrayList<>();
        // 查询当月领用单
        OrderQueryRequest request = new OrderQueryRequest();
        request.setGmtStart(DateUtil.getDateStr(DateUtil.currentMonthly(), DateUtil.DATE_FORMATTER_YMD));
        request.setFirmId(firmId);
        List<OrderDO> orders = this.orderService.getList(request, OrderTypeEnum.ORDER);
        // 只统计有工单的领料单
        orders = orders.stream().filter(f -> StringUtils.hasText(f.getWorkOrderSn())).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(orders)) {
            return Collections.emptyList();
        }

        Map<Long, List<OrderDO>> orderMap = orders.stream().collect(Collectors.groupingBy(OrderDO::getDeviceId));
        List<DeviceDO> devices = this.deviceService.getDevices(Lists.newArrayList(orderMap.keySet()), null, null, CommonConstants.ONE);

        if (CollectionUtils.isEmpty(devices)) {
            return Collections.emptyList();
        }

        Map<Long, DeviceDO> deviceMap = devices.stream().collect(Collectors.toMap(DeviceDO::getId, f -> f));

        orderMap.forEach((key, val) -> {
            DeviceDO device = deviceMap.get(key);
            if (device == null) {
                return;
            }

            List<String> orderSns = val.stream().map(OrderDO::getOrderSn).collect(Collectors.toList());
            OrderDetailDO orderDetailDO = new OrderDetailDO();
            orderDetailDO.setOrderSnList(orderSns);
            List<OrderDetailDO> orderDetails = this.orderDetailService.queryListByOrderSn(orderDetailDO);

            BigDecimal amount = getAmount(orderDetails);
            int recipientNum = val.stream().map(OrderDO::getUserId).collect(Collectors.toSet()).size();
            int skuNum = orderDetails.stream().mapToInt(OrderDetailDO::getQty).sum();

            industryDeviceSummaryVOS.add(new IndustryDeviceSummaryVO(device.getId(), device.getDeviceName(), amount, recipientNum, skuNum));
        });

        // 根据领用金额倒排
        industryDeviceSummaryVOS.sort(Comparator.comparing(IndustryDeviceSummaryVO::getAmount).reversed());
        // 取top x
        return industryDeviceSummaryVOS.subList(0, Math.min(count, industryDeviceSummaryVOS.size()));
    }

    private void wrapDeviceInfo(List<DeviceDO> devices, IndustryOverviewVO industryOverview) {
        // 主设备
        List<DeviceDO> mainDevices = devices.stream().filter(f -> CommonConstants.ONE.equals(f.getIsMaster())).collect(Collectors.toList());
        List<DeviceDO> onlineDevices = mainDevices.stream()
                .filter(f -> f.getGmtConnect() != null && DateUtil.diffTime(f.getGmtConnect(), new Date(), TimeUnit.MINUTES) < CommonConstants.FIFTEEN).collect(Collectors.toList());

        List<Long> deviceIds = devices.stream().map(DeviceDO::getId).collect(Collectors.toList());
        // 设备货道
        List<CargowayDO> cargowayDOS = this.cargowayService.getCargowayList(deviceIds);
        // 设备商品
        List<CargowaySkuDO> cargowaySkuDOS = this.cargowaySkuService.getDeviceSkuList(deviceIds);
        Integer totalSkuInventory = cargowaySkuDOS.stream().map(CargowaySkuDO::getInventory).mapToInt(f -> f).sum();
        Integer totalCargowayVol = cargowayDOS.stream().map(CargowayDO::getCargowayVol).mapToInt(f -> f).sum();
        BigDecimal stockRate = BusinessUtil.getStockRate(totalSkuInventory,totalCargowayVol);

        industryOverview.setDeviceNum(mainDevices.size()).setOnlineDeviceNum(onlineDevices.size()).setStockRate(stockRate + "%");
    }

    private void wrapOrderInfo(Long firmId, Date fromDate, IndustryOverviewVO industryOverview) {

        // 查询当天工单
        List<WorkOrderDO> workOrders = workOrderService.firstPage(firmId, fromDate, Integer.MAX_VALUE);

        if (CollectionUtils.isEmpty(workOrders)) {
            return;
        }
        List<String> workOrderSns = workOrders.stream().map(WorkOrderDO::getWorkOrderSn).collect(Collectors.toList());
        List<WorkOrderDetailDO> workOrderDetails = workOrderDetailService.getWorkOrderDetails(null, null, workOrderSns);
        // 查询对应的领料单
        QuotationQueryRequest orderQueryRequest = new QuotationQueryRequest();
        orderQueryRequest.setWorkOrderSns(workOrderSns);
        List<OrderDO> orders = this.orderService.orderQuoteOrders(orderQueryRequest);
        List<OrderDetailDO> orderDetails = getOrderDetails(orders);

        int overNum = CommonConstants.ZERO;
        for (WorkOrderDO workOrder:workOrders) {
            List<OrderDO> curOrders = orders.stream().filter(f -> f.getWorkOrderSn().equals(workOrder.getWorkOrderSn())).collect(Collectors.toList());
            Set<String> curOrderSns = curOrders.stream().map(OrderDO::getOrderSn).collect(Collectors.toSet());
            List<OrderDetailDO> curOrderDetails = orderDetails.stream().filter(f -> curOrderSns.contains(f.getOrderSn())).collect(Collectors.toList());
            List<WorkOrderDetailDO> curWorkOrderDetails = workOrderDetails.stream().filter(f -> f.getWorkOrderSn().equals(workOrder.getWorkOrderSn())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(curOrders) || CollectionUtils.isEmpty(curOrderDetails) || CollectionUtils.isEmpty(curWorkOrderDetails)) {
                continue;
            }
            Set<String> workOrderSkuCodes = curWorkOrderDetails.stream().map(WorkOrderDetailDO::getSkuCode).collect(Collectors.toSet());
            Set<String> orderSkuCodes = curOrderDetails.stream().map(OrderDetailDO::getSkuCode).collect(Collectors.toSet());

            int workOrderQty = curWorkOrderDetails.stream().map(WorkOrderDetailDO::getPlanQty).mapToInt(f -> f).sum();
            int orderQty = curOrderDetails.stream().map(OrderDetailDO::getQty).mapToInt(f -> f).sum();

            if (orderQty > workOrderQty || !workOrderSkuCodes.containsAll(orderSkuCodes)) {
                overNum = overNum + CommonConstants.ONE;
            }
        }
        // 从商品中心取面价
        BigDecimal amount = getAmount(orderDetails);

        industryOverview.setApplyOrderNum(workOrders.size()).setOverNum(overNum).setAmount(amount);
    }

    private BigDecimal getAmount(List<OrderDO> orders, Map<String, BigDecimal> productPriceMap, List<OrderDetailDO> orderDetails) {

        if (CollectionUtils.isEmpty(orders)) {
            return new BigDecimal("0.00");
        }

        List<String> orderSns = orders.stream().map(OrderDO::getOrderSn).collect(Collectors.toList());
        List<OrderDetailDO> curOrderDetails = orderDetails.stream().filter(od -> orderSns.contains(od.getOrderSn())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(curOrderDetails)) {
            return new BigDecimal("0.00");
        }
        return curOrderDetails.stream().map(f -> new BigDecimal(f.getQty()).multiply(productPriceMap.get(f.getSkuCode()))).reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private BigDecimal getAmount(List<OrderDetailDO> orderDetails) {
        return orderDetails.stream().map(f -> new BigDecimal(f.getQty()).multiply(BigDecimal.ZERO)).reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private List<OrderDetailDO> getOrderDetails(List<OrderDO> orders) {
        List<OrderDetailDO> orderDetails = new ArrayList<>();
        if (!CollectionUtils.isEmpty(orders)) {
            List<String> orderSns = orders.stream().map(OrderDO::getOrderSn).collect(Collectors.toList());
            OrderDetailDO orderDetailQueryParam = new OrderDetailDO();
            orderDetailQueryParam.setOrderSnList(orderSns);
            orderDetails = this.orderDetailService.queryListByOrderSn(orderDetailQueryParam);
        }
        return orderDetails;
    }

    private void wrapReplenishmentInfo(Long firmId, IndustryOverviewVO industryOverview) {
        ReplenishmentDO replenishmentQueryParam = new ReplenishmentDO();
        replenishmentQueryParam.setFirmId(firmId);
        replenishmentQueryParam.setRsStatusList(Lists.newArrayList(ReplenishmentStatusEnum.PENDING.getCode(), ReplenishmentStatusEnum.PARTIALLY_COMPLETED.getCode()));
        List<ReplenishmentDO> replenishmentDOS = this.replenishmentService.findByModel(replenishmentQueryParam, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
        // 今日新建
        replenishmentQueryParam.setRsStatusList(null);
        replenishmentQueryParam.setGmtCreatedStart(DateUtil.currentDaily());
        List<ReplenishmentDO> newReplenishmentDOS = this.replenishmentService.findByModel(replenishmentQueryParam, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);

        industryOverview.setInTransitReplenishmentNum(replenishmentDOS.size()).setNewReplenishmentNum(newReplenishmentDOS.size());
    }



}

